/**
 * @author A. Lothore
 * @classDescription GUI core object : base class of all data objects lists
 */

 GUIList.prototype = {};
 
 function GUIList()
 {
	this.client_id    	= getNewGUIId();
	this.application_id	= null;
	this.visible      	=	false;
	this.object_type  	= "GenericObject";
	this.container    	= [];
	
	//Internal, used to differenciate lists and objects
	this.isList = true;
	
	//Status
	this.status     = "ready"; //ready or loading
	this.setStatus  = GUI_setStatus;
	this.ready      = GUIList_ready;
	this.onReady    = function() {};
	
	//Views
	this.goToView     = GUIList_goToView;
	this.currentView  = null; //a view name //TODO : still used ??
	
	//Show-hide
	this.show     = GUIList_show;
	this.hide     = GUIList_hide;
	this.refesh   = GUIList_Refresh;
	
	//Elements
	this.elements       = [];
	this.addElement     = GUIList_addElement;
	this.moveElement    = GUIList_moveElement;
	this.removeElement  = GUIList_removeElement;
	this.parentGUI      = null;
	this.setParentGUI   = GUIList_setParentGUI;
	this.empty          = GUIList_empty;
	
	//Navigation
	this.getPrevious  = GUIList_getPrevious;
	this.getNext      = GUIList_getNext;	  
	this.getFirst     = GUIList_getFirst;
	this.getLast      = GUIList_getLast;
	
	//Patterns
	this.getPatternId = GUIList_getPatternId;
	this.attachToDOM  = GUIList_attachToDOM;
	
	//Data Access
	this.serverInterface  = new ServerInterface();
	this.setDataSource    = GUIList_setDataSource;
	this.getDataSource    = GUIList_getDataSource;
	this.setObjectType    = GUIList_setObjectType;
	this.setApplicationId = GUIList_setApplicationId;
	this.setCallMethod    = GUIList_setCallMethod;
	this.getCallMethod    = GUIList_getCallMethod;
	this.loadData         = GUIList_loadData;
	this.onRemoteCall     = function() {};
	this.bindData         = GUIList_bindData;
	this.getParams        = GUIList_getParams;
	this.fromJSON         = GUIList_fromJSON;
	this.insertEmpty      = GUIList_insertEmpty;
	
	//Misc
	this.getContainer   = GUIList_getContainer;
	this.setContainer   = GUIList_setContainer;
	this.setDragAndDrop = GUIList_setDragAndDrop;
	
	//User defined function (set in HTML doc)
	this.userShowFunction = null;
	this.userHideFunction = null;
	this.setUserShowFunction = GUIList_setUserShowFunction; // TODO : replace by custom events ?
	this.setUserHideFunction = GUIList_setUserHideFunction; // TODO : replace by custom events ?
	
	//Private
	this.applyOnElements    	= GUIList_applyOnElements;
	this.applyOnContainers  	= GUIList_applyOnContainers;
	this.getDragAcceptClasses	= GUIList_getDragAcceptClasses;	
	
	//Intialize
	addToGUIs(this);
}
 
//--------------------------------------------------------------------------------------------------------------------------
/*
 * Show all elements of the list (by calling show function of each Gui object)
 * @viewName : the name of the view to show. If no viewName, the defaultView will be shown. If no defaultView, then nothing happens.
 * @showFunction : the function used to show each element (e.g. : for animation)
 */
function GUIList_show(viewName,showFunction)
{
	this.applyOnElements(function(_this,elem) {
                          elem.show(viewName,showFunction || this.userShowFunction);
                        });
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Hide all elements of the list (by calling hide function of each Gui object)
 * @viewName : the name of the view to hide. If no viewName, the defaultView will be hidden. If no defaultView, then nothing happens.
 * @hideFunction : the function used to hide (e.g. : for animation)
 */
function GUIList_hide(viewName,hideFunction)
{
	this.applyOnElements(function(_this,elem) {
                          elem.hide(viewName,hideFunction || this.userHideFunction);
                        });	
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Set the user-defined show function for elements in the list
 * @showFunction : the user-defined function name used to show (e.g. : for animation)
 */
function GUIList_setUserShowFunction(showFunction)
{
	this.userShowFunction = showFunction;
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Set the user-defined hide function for elements in the list
 * @hideFunction : the user-defined function name used to hide (e.g. : for animation)
 */
function GUIList_setUserHideFunction(hideFunction)
{
	this.userHideFunction = hideFunction;
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Refresh all views of all elements of the list (by calling refresh function of each Gui object)
 */
function GUIList_Refresh()
{
	this.applyOnElements(function(_this,elem) {
							elem.refresh();
						  });
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Show a specific view off all elements of the list, and hide all others (by calling goToView function of each Gui object)
 * @viewName : the view name to show
 */
function GUIList_goToView(viewName)
{
	this.applyOnElements(function(_this,elem) {
                          elem.goToView(viewName);
                        });
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Adds an element to a the data objects list
 * @elementToAdd : the Gui Object to add
 * @position : position of added object within list. Can be first (first of list), last (last of list) or an other GUIObject (elementToAdd will be put after)
 */
function GUIList_addElement(elementToAdd,position)
{
  //In case position is an other GUI object
  if (position && position.object_type)
  {
    //find index of "position" GUI Object
    var index;
    for (element in this.elements)
    {
      if (this.elements[element].client_id == position.client_id)
      {
        index = element;
        break;
      }
    }    
    //Insertion
    this.elements.splice(index,0,elementToAdd);
  }  
  else
  {
    //Other cases
    switch (position)
    {
      case "first":
        this.elements.splice(0,0,elementToAdd);
        break;
      case "last":
        this.elements[this.elements.length] = elementToAdd;
        break;
      default :
        this.elements[this.elements.length] = elementToAdd;
    }
  }  
  
	elementToAdd.setParentList(this);
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Removes an element from a list of data objects
 * ! Warning : doesn't delete views : must be done prior to this
 * @elementToRemove : the Gui object to remove
 */
function GUIList_removeElement(elementToRemove)
{
  //find index of element to delete
	var index;
	for (element in this.elements)
	{
		if (this.elements[element].client_id == elementToRemove.client_id)
		{
			index = element;
			break;
		}
	}
	if (index) 
    {this.elements.splice(index,1);}
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Moves an element from an other list of data objects
 * @elementToMove : the Gui object to move
 * @position : position of moved object within target list. Can be first (first of list), last (last of list) or an other GUIObject (elementToMove will be put after)
 */
function GUIList_moveElement(elementToMove,position)
{
	var previousList = elementToMove.parentList;
	previousList.removeElement(elementToMove);
	this.addElement(elementToMove,position);
  
  //Sets element rank in new list
  elementToMove.setRank();
	
  //Delete currents views
  var currentViews = elementToMove.currentViews.splice(0);
  
  //Delete all views in DOM
	elementToMove.deleteViews();
  
  //Show inserted element
	applyOnArray(this,currentViews,function(_this,elem){
                                      elementToMove.show(elem);
                                  });
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Adds an empty element to the list. Used for creation of a new element.
 * @position : position of new object within target list. Can be first (first of list), last (last of list) or an other GUIObject (new element will be put after)
 * @dataType : the dataType of the new object to insert (if the list is set to contain only one dataType, then this one will be used instead)
 */
function GUIList_insertEmpty(position,dataType)
{
  var tmpGUI = new GUI();
  if (this.object_type && this.object_type != defaultSettings.untypedList && this.object_type.indexOf(",") == -1) //last test : not a list of dataTypes
	tmpGUI.object_type = this.object_type;
  else
	tmpGUI.object_type = dataType;
  tmpGUI.setDataSource(this.serverInterface.dataSource);
	this.addElement(tmpGUI,position);
  tmpGUI.setRank();
	return tmpGUI;
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Sets the parent GUI of a list of data objects
 * @parentGUIToSet : the Gui object to set as parent
 */
function GUIList_setParentGUI(parentGUIToSet)
{
   this.parentGUI = parentGUIToSet;
   this.attachToDOM();
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Attach to list pattern DOM object
 * @_listContainer : the DOM object to set as container
 */
function GUIList_attachToDOM(_listContainer)
{
  var DOMObjectContainer = null;
  var listContainer = this.getContainer("");
    
  if (!listContainer) 
    {listContainer = _listContainer;}
  
  if (!listContainer)
  {
	  //If the parent GUI is a simple GUI : search pattern in available views
	  if (this.parentGUI && this.parentGUI.parentList) 
	  {
	    applyOnArray(this,this.parentGUI.currentViews,
	              function(_this,currentViewName) {
	                DOMObjectContainer = _this.parentGUI.getView(currentViewName);
	                if (!listContainer)  
                    {listContainer = getContainerPatternObject(_this,_this.getPatternId(),DOMObjectContainer);}
	              });
		 //If not found : search pattern in entire document
	    if (!listContainer) 
        {listContainer = getContainerPatternObject(this,this.getPatternId());}
	  }
	  else
	  {
	    //If the parent is a GUIList : search pattern in list container
	    if (this.parentGUI) 
	    {
	      DOMObjectContainer = this.parentGUI.getContainer("");
	      listContainer = getContainerPatternObject(this,this.getPatternId(),DOMObjectContainer);
	    }
	    //If no parent GUI : search pattern in entire document
	    else 
	    {
	      listContainer = getContainerPatternObject(this,this.getPatternId());
	    }
	  }
  }
  if (listContainer) 
    {listContainer.guiid = this.client_id;}
  this.setContainer("",listContainer);
  //Iframe for remote calls
  if (this.serverInterface.callMethod == 'iframe')
    {addIframe(listContainer,this);}
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Empty the list of data objects (remove views)
 */

function GUIList_empty()
{
	this.applyOnElements(function(_this,elem) {
							elem.deleteViews();	  
						  });
	this.elements = [];
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Get the previous Gui object in list
 * @curObj : the current Gui object
 */
function GUIList_getPrevious(curObj)
{
	var prevObj;
	var i = 1;
	var tmpObj = this.elements[0];
  while (!prevObj && i<this.elements.length)
	{
		if (this.elements[i].client_id == curObj.client_id)
      {prevObj = tmpObj;}
		tmpObj = this.elements[i];
		i++;
	}
	return prevObj;	
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Get the next Gui object in list
 * @curObj : the current Gui object
 */
function GUIList_getNext(curObj)
{
	var nextObj;
	var i = 0;
	while (!nextObj && i<this.elements.length)
	{
		if (i<this.elements.length && this.elements[i].client_id == curObj.client_id)
      {nextObj = this.elements[i+1];}
		i++;
	}
	return nextObj;	
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Get the first Gui object in list
 */
function GUIList_getFirst()
{
	return this.elements[0];
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Get the last Gui object in list
 */
function GUIList_getLast()
{
	return this.elements[this.elements.length-1];	
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Set the object type
 * @objectType : the object type value to set
 */
function GUIList_setObjectType(objectType)
{
	this.object_type = objectType;
} 

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Set the application id
 * @applicationId : the application ID value to set
 */
function GUIList_setApplicationId(applicationId)
{
	this.application_id = applicationId;
} 


//--------------------------------------------------------------------------------------------------------------------------
/**
 * Set the datasource url (a quick access to the dataSource attribute of GuiList's serverInterface attribute)
 * @dataSource : the dataSource url to set
 */
function GUIList_setDataSource(dataSource)
{
	this.serverInterface.setDataSource(dataSource);
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Get the datasource url (a quick access to the dataSource attribute of GuiList's serverInterface attribute)
 * @dataSource : the dataSource url to get
 */
function GUIList_getDataSource()
{
	return this.serverInterface.getDataSource();
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Set the call method (a quick access to the callMethod attribute of GuiList's serverInterface attribute)
 * @dataSource : the dataSource url to set
 */
function GUIList_setCallMethod(callMethod)
{
	this.serverInterface.setCallMethod(callMethod);
}

//--------------------------------------------------------------------------------------------------------------------------
/**
 * Get the call method (a quick access to the callMethod attribute of GuiList's serverInterface attribute)
 */
function GUIList_getCallMethod()
{
	return this.serverInterface.getCallMethod();
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Returns the pattern id (i.e. the id of the DOM object to use as a pattern)
 * @viewName : the name of the view to get its pattern id
 */
function GUIList_getPatternId(viewName)
{
  if (viewName && viewName!= "") 
    {return "pattern_" + this.object_type + "_" + viewName;} //case of GUI patterns
  else 
    {return "pattern_" + this.object_type;} //case of GUIList pattern (container)
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Load the List of Gui objects by calling the dataSource set, using the callMethod.
 */
function GUIList_loadData()
{
	this.empty();
	this.attachToDOM();
	this.setStatus("loading");
	this.onRemoteCall = function (jsonData) {
                    this.bindData(jsonData);
                };
	//TODO : handle other remote methods ?
	this.serverInterface.remoteCall("list",this.getParams(),this.client_id);  
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Get the param list for GuiList's loadData : object_type, list GuiId and parent GuiId.
 */
function GUIList_getParams()
{
   var params = "object_type=" + this.object_type;
   params +="&client_id=" + this.client_id;
   if (this.parentGUI && this.parentGUI.object_id) 
	{params += "&parent=" + this.parentGUI.object_id;}
   else
	{params += "&parent=" + this.application_id;}
   return params;
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Get the datName object from the json response and bind it as a list of Gui objects
 * Simple call to the jsonMethod
 * @jsonData : the json response object
 */
function GUIList_bindData(jsonData)
{
	if (jsonData) 
	{
		this.fromJSON(jsonData.result);
		
		if (jsonData.error)
			{defaultSettings.displayMessage(jsonData.error,"E");}
		if (jsonData.debug)
			{GG.api.log(jsonData.debug,"I");}			
	}
}	

//--------------------------------------------------------------------------------------------------------------------------
/* 
 * Set a callback function to trigger when the list has been loaded. The callback function will be used once.
 * @onReady : the callback function to set
 */
function GUIList_ready(onReady)
{
  this.onReady = function() {onReady();};
}

//--------------------------------------------------------------------------------------------------------------------------
/* 
 * Set the list status.
 * Setting it to "ready" will trigger the onReady function and remove it.
 * //TODO : why remove the onReady callback ??
 * //TODO : add loading behaviour : (default loading indicator)
 * @newStatus :  : the new status to set can be "ready" or "loading"
 */
function GUI_setStatus(newStatus)
{
  this.status = newStatus;
  if (this.status == "ready" && this.onReady)
  {
    this.onReady();
    this.onReady = function() {};
  }
}

//--------------------------------------------------------------------------------------------------------------------------
/* 
 * Convert a json response into a list of Gui objects
 * Each Gui object is constructed using the fromJson method
 * @newStatus :  : the new status to set can be "ready" or "loading"
 */ 
function GUIList_fromJSON(jsonData)
{
	for (var i=0;i<jsonData.length;i++)
	{
		tmpGUI = new GUI(jsonData[i]);
		tmpGUI.setDataSource(this.serverInterface.dataSource);
		this.addElement(tmpGUI);
	}
	this.setStatus("ready");
}

//--------------------------------------------------------------------------------------------------------------------------
/* 
 * Apply a function to every Gui object of the list
 * @functionToApply : the function to apply
 */ 
function GUIList_applyOnElements(functionToApply)
{
  applyOnArray(this,this.elements,functionToApply);
}

//--------------------------------------------------------------------------------------------------------------------------
/* 
 * Set the container DOM object for a specific view
 * @viewName : the view name to set
 * @containerObj : the DOM object to set as the container
 */ 
function GUIList_setContainer(viewName, containerObj)
{
	this.container[viewName] = containerObj;
  this.setDragAndDrop(viewName);
}

//--------------------------------------------------------------------------------------------------------------------------
/* 
 * Get the container DOM object for a specific view
 * @viewName : the view name to get
 */ 
function GUIList_getContainer(viewName)
{
	return this.container[viewName];
}

//--------------------------------------------------------------------------------------------------------------------------
/* 
 * Apply a function to all view containers
 * @functionToApply : the function to apply
 */ 
function GUIList_applyOnContainers(functionToApply)
{
  applyOnArray(this,this.container,functionToApply);
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Drag & Drop function
 * Based on Interface plugin for jQuery
 * @viewName : the viewName to apply D&D on
 */
function GUIList_setDragAndDrop(viewName)
{
  var viewObj = this.container[viewName];
  var cssAcceptList = this.getDragAcceptClasses();
  //Droppable DOM Object (for D&D on empty list or at the end of a list)
  GG.api.log("List" + this.client_id + " accepts drops with css class '"+ cssAcceptList + "'" );
  $(viewObj).droppable(
                {
                  accept : cssAcceptList, 
                  hoverClass : "dragAndDropHoverList",
                  greedy : true,
                  tolerance: defaultSettings.dropTolerance || 'intersect',
                  drop : function (event,ui) {
                            var dataObjectDragged = GG.api.getGG(ui.draggable.get(0));
                            
                            var dataObjectDrop = GG.api.getGGList(this);
                            dataObjectDrop.moveElement(dataObjectDragged,"last");
                            dataObjectDragged.save();
                    }
                });  
}

//--------------------------------------------------------------------------------------------------------------------------
/*
 * Converts the object_type list string into a list of accepted drag css classes (string)
 */
function GUIList_getDragAcceptClasses()
{
  var cssAcceptList = "";
  if (this.object_type)
  {
	if (this.object_type === defaultSettings.untypedList)
	{
		cssAcceptList = "*";
	}
	else
	{
		var dataTypesArray = this.object_type.split(",");		
		var separ = "";
		applyOnArray(this,dataTypesArray,function(arrayObj,arrayElem)
											{
												cssAcceptList += separ + "." + defaultSettings.dragClassPrefix +arrayElem;
												separ = ",";
											});
	}
  }
  return cssAcceptList;
}